package com.zhz.common.tool.oss.aliyun;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectResult;
import com.zhz.common.tool.oss.OssService;
import com.zhz.common.tool.oss.huaweicloud.VO.FileUploadStatusVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Objects;

/**
 * 阿里云上传服务
 *
 * @author zhouhengzhe
 */
@Slf4j
public class AliyunOssServiceImpl implements OssService {

    private final OSS ossClient;
    private final AliyunOssProperties aliyunOssProperties;

    public AliyunOssServiceImpl(AliyunOssProperties aliyunOssProperties) {
        checkAliyunOssProperties(aliyunOssProperties);
        this.ossClient = new OSSClientBuilder()
                .build(aliyunOssProperties.getEndpoint(),
                        aliyunOssProperties.getAccessKeyId(),
                        aliyunOssProperties.getAccessKeySecret());
        this.aliyunOssProperties = aliyunOssProperties;
    }

    /**
     * 检查阿里云配置是否为空
     *
     * @param aliyunOssProperties 阿里云配置
     */
    private static void checkAliyunOssProperties(AliyunOssProperties aliyunOssProperties) {
        if (Objects.isNull(aliyunOssProperties)) {
            throw new RuntimeException("阿里云OSS配置为空");
        }
        if (Objects.isNull(aliyunOssProperties.getAccessKeyId())) {
            throw new RuntimeException("阿里云OSS配置accessKeyId为空");
        }
        if (Objects.isNull(aliyunOssProperties.getAccessKeySecret())) {
            throw new RuntimeException("阿里云OSS配置accessKeySecret为空");
        }
        if (Objects.isNull(aliyunOssProperties.getEndpoint())) {
            throw new RuntimeException("阿里云OSS配置endpoint为空");
        }
    }

    /**
     * 文件(https://help.aliyun.com/document_detail/84781.htm)
     *
     * @param uploadFile 上传的文件
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @return
     */
    @Override
    public String uploadFile(MultipartFile uploadFile, String fileName, String bucketName) {
        try {
            createBucket(bucketName);
            PutObjectResult putObjectResult = ossClient.putObject(fileName, bucketName, uploadFile.getInputStream());
            //检查返回值结果
            checkPutObject(putObjectResult);
            return "http://" + bucketName + "." + aliyunOssProperties.getEndpoint() + "/" + fileName;
        } catch (Exception e) {
            throw new RuntimeException("文件上传失败");
        }
    }


    /**
     * 检查返回值结果
     *
     * @param putObjectResult 返回值结果
     */
    private static void checkPutObject(PutObjectResult putObjectResult) {
        if (Objects.isNull(putObjectResult) ||
                Objects.isNull(putObjectResult.getResponse()) ||
                putObjectResult.getResponse().getStatusCode() != 200) {
            log.error("上传文件返回的参数为：{}", JSON.toJSON(putObjectResult));
            throw new RuntimeException("上传文件失败");
        }
    }


    /**
     * 上次文件流
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @param out        文件流
     * @return
     */
    @Override
    public String uploadFileByte(String fileName, String bucketName, byte[] out, String contentType) {
        try {
            createBucket(bucketName);
            //检查返回值结果
            PutObjectResult putObjectResult = ossClient.putObject(bucketName, fileName, new ByteArrayInputStream(out));
            checkPutObject(putObjectResult);
            return "http://" + bucketName + "." + aliyunOssProperties.getEndpoint() + "/" + fileName;
        } catch (Exception e) {
            throw new RuntimeException("上次文件流失败");
        }
    }

    /**
     * 获取文件上传进度（https://help.aliyun.com/document_detail/84796.htm?spm=a2c4g.408162.0.0.3dfdad8bHsy8lg）
     *
     * @param fileName 文件名称
     * @return 返回文件上传进度
     */
    @Override
    public FileUploadStatusVO getFileUploadPlan(String fileName, String bucketName) {
        throw new RuntimeException("阿里云暂不支持接口获取进度条，请去https://help.aliyun.com/document_detail/84796.htm?spm=a2c4g.408162.0.0.3dfdad8bHsy8lg查看文档使用");
    }

    /**
     * 文件下载
     *
     * @param response 响应
     * @param fileName 文件名称
     */
    @Override
    public void fileDownload(HttpServletResponse response, String fileName, String bucketName) {
        try {
            OSSObject ossObject = ossClient.getObject(bucketName, fileName);
            InputStream in = ossObject.getObjectContent();
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            throw new RuntimeException("文件下载失败");
        }
    }

    /**
     * 删除文件
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     */
    @Override
    public void deleteFile(String fileName, String bucketName) {
        ossClient.deleteObject(bucketName, fileName);
    }

    /**
     * 不存在就创建桶
     *
     * @param bucketName 桶名称
     */
    private void createBucket(String bucketName) {
        //不存在桶
        if (!checkBucket(bucketName)) {
            //创建一个桶,设置仓库名称
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            //设置仓库权限公共读
            createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
            ossClient.createBucket(createBucketRequest);
        }
    }

    /**
     * 获取上传地址
     *
     * @param bucketName 桶名称
     * @param fileName   文件名称
     * @return
     */
    private String getUploadUrl(String bucketName, String fileName) {
        return "http://" + bucketName + "." + aliyunOssProperties.getEndpoint() + "/" + fileName;
    }

    /**
     * 判断是否存在桶
     *
     * @param bucketName 桶名称
     * @return 返回是否存在
     */
    public boolean checkBucket(String bucketName) {
        return ossClient.doesBucketExist(bucketName);
    }

}
